/*
 *   Copyright 2021 zzh
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 */

package com.aduib.boot.common.SPI;

import com.aduib.boot.common.SPI.metadata.ConstructorMetaData;
import com.aduib.boot.common.SPI.metadata.MethodMetaData;
import com.aduib.boot.common.util.ClassUtils;
import com.aduib.boot.common.util.StrUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * SPI工厂工具
 *
 * @description: SPILoder
 * @author: zzh
 * @date: 2021/9/18 14:43
 */
public class FactoryUtils {

  public static boolean withSpiAnnotation(Class<?> type) {
    return type.isAnnotationPresent(SPI.class);
  }

  public static boolean withAdaptiveAnnotation(Method[] methods) {
    boolean result = false;
    for (Method method : methods) {
      if (method.isAnnotationPresent(Adaptive.class)) {
        result = true;
        break;
      }
    }
    return result;
  }

  public static boolean withAdaptiveAnnotation(Class<?> clazz) {
    return clazz.isAnnotationPresent(Adaptive.class) || withAdaptiveAnnotation(clazz.getMethods());
  }

  public static boolean withWrapperAnnotation(Class<?> type) {
    return type.isAnnotationPresent(Wrapper.class);
  }

  public static ClassLoader findClassLoader() {
    return ClassUtils.getDefaultClassLoader(SPIFactory.class);
  }

  /** get value of adaptive annotation or if empty return splitted simple name */
  public static String[] getMethodAdaptiveValue(Adaptive adaptiveAnnotation, Class<?> type) {
    String[] value = adaptiveAnnotation.value();
    // value is not set, use the value generated from class name as the key
    if (value.length == 0) {
      List<String> strings = StrUtils.splitTrim(type.getSimpleName(), ".");
      Collections.reverse(strings);
      if (!strings.isEmpty()) {
        String splitName = strings.get(0);
        value = new String[] {splitName};
      }
    }
    return value;
  }

  public static String[] getMethodAdaptiveValue(
      SPI spiAnnotation, Adaptive adaptiveAnnotation, Class<?> interfaceClass) {
    String[] value = adaptiveAnnotation.value();
    String spiName = spiAnnotation.value();
    // value is not set, use the value generated from class name as the key
    if (value.length == 0) {
      List<String> strings = StrUtils.splitTrim(interfaceClass.getSimpleName(), ".");
      if (StrUtils.isNotBlank(spiName)) {
        strings.add(spiName);
      }
      Collections.reverse(strings);
      if (!strings.isEmpty()) {
        String splitName = strings.get(0);
        value = new String[] {splitName};
      }
    }
    return value;
  }

  /**
   * 常见
   * @param cm
   * @return
   */
  @SuppressWarnings("unchecked")
  public static Object newSPIInstance(ConstructorMetaData cm) {
    if (Objects.isNull(cm)) {
      return null;
    }
    if (Objects.isNull(cm.getAccessibleObject())) {
      return null;
    }
    return ClassUtils.newInstance((Constructor<?>) cm.getAccessibleObject(), cm.getParameters());
  }

  //判断是否是set方法
  public static boolean isSetter(MethodMetaData methodMetaData) {
    return methodMetaData.getSimpleName().startsWith("set")
            && methodMetaData.getParameterTypes().length == 1F
            && Modifier.isPublic(methodMetaData.getModifiers());
  }
}
